<?php
/**
 * (logic)
 */
namespace App\Logic\Amazon;
use App\Libs\MarketplaceWebService\ReportMws;
use App\Libs\RequestClient;
use App\Models\Amazon\ReportLoad;
use App\Models\Amazon\ReportRequest;
use Illuminate\Support\Facades\DB;
class GrabReport extends GrabCommon
{
    /**
     * 提交报告请求
     * API:RequestReport
     * @param $account
     * @param $typeId
     * @param array $con
     * @return array|bool
     */
    public static function requestReport($account,$typeId,$con = array()){
        if(!$account)return false;
        if(!$typeId)return false;
        if(!array_key_exists($typeId,ReportType::$_reportTypeList))return false;
        $reportType = ReportType::$_reportTypeList[$typeId];
        $con['ReportType'] = $reportType[ReportType::KEY_TYPE];
        $con['Merchant'] = $account->merchant;
        $con['MWSAuthToken'] = $account->mws_auth_token;

        //存在取值间隔时间条件时，重新排列时间参数
        if($reportType[ReportType::KEY_INTERVAL] > 0){
            if(!(isset($con['StartDate']) && $con['StartDate'])){
                $con['StartDate'] = date('Y-m-d 00:00:00',strtotime("-".$reportType[ReportType::KEY_INTERVAL]." days"));
            }
            if(!(isset($con['EndDate']) && $con['EndDate'])){
                $con['EndDate'] = date('Y-m-d 00:00:00',time());
            }
            if(strtotime($con['StartDate']) >= strtotime($con['EndDate']))return false;
            $con['StartDate'] = getDateFormat($con['StartDate']);
            $con['EndDate'] = getDateFormat($con['EndDate']);
        }

        //3: 实时库存报告
        if($typeId == 3){
            //排除账号国家：（不需要统计）
            //A1AM78C64UM0Y8(NA-北美区)MX 墨西哥
            //A2Q3Y263D00KWC(NA-北美区)BR 巴西
            //A1C3SOZRARQ6R3(EU-欧洲区)PL 波兰
            //A1805IZSGTT6HS(EU-欧洲区)NL 荷兰
            //A2NODRKZP88ZB9(EU-欧洲区)SE 瑞典
            //A33AVAJ2PDY3EV(EU-欧洲区)TR 土耳其
            //AMEN7PMS3EDWL (EU-欧洲区)BE 比利时
            $marketplaceIdList = $account->account_country->whereNotIn('marketplace_id',
                ['A1AM78C64UM0Y8','A2Q3Y263D00KWC','A1C3SOZRARQ6R3','A1805IZSGTT6HS','A2NODRKZP88ZB9','A33AVAJ2PDY3EV','AMEN7PMS3EDWL'])
                ->pluck('marketplace_id')->toArray();
        }else{
            //排除账号国家：（不需要统计）
            //A1AM78C64UM0Y8(NA-北美区)MX 墨西哥
            $marketplaceIdList = $account->account_country->where('marketplace_id','<>','A1AM78C64UM0Y8')->pluck('marketplace_id')->toArray();
        }

        $service = self::getRequestClient('report',$account);
        $rm = ReportMws::getInit();
        $re = [];
        $count = count($marketplaceIdList);
        DB::connection('mysql_amazon')->beginTransaction();
        $sFlag = false;

        //报告是否区分端点（站点）
        if($reportType[ReportType::KEY_MULTI] > 0){
            $s = 0;
            foreach ($marketplaceIdList as $marketplaceId){
                $con['MarketplaceIdList'] = $marketplaceId;
                $param = $rm->requestReport($con);
                $i = 1;
                $flag = false;
                while (!$flag && $i <= 3){
                    $data = $service->requestExe($param);
                    $flag = self::_extractRequestReportResponse($account->id,$data,$marketplaceId);
                    if(!$flag)$i++;
                    sleep(2);
                }
                if($flag)$s++;
                $re[$marketplaceId]= $flag;
            }
            if($s == $count)$sFlag = true;
        }else{
            $con['MarketplaceIdList'] = $marketplaceIdList;
            $param = $rm->requestReport($con);
            $i = 1;
            $flag = false;
            while (!$flag && $i <= 3){
                $data = $service->requestExe($param);
                $flag = self::_extractRequestReportResponse($account->id,$data);
                if(!$flag)$i++;
                sleep(2);
            }
            $re[implode('-',$marketplaceIdList)] = $flag;
            if($flag)$sFlag = true;
        }
        if($sFlag){
            DB::connection('mysql_amazon')->commit();
        }else{
            DB::connection('mysql_amazon')->rollBack();
        }
        return $re;
    }

    /**
     * 添加报告数据（请求响应的报告信息）
     * @param $accountId
     * @param $data
     * @param null $marketplaceId
     * @return false|\Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Model|object
     */
    private static function _extractRequestReportResponse($accountId,$data,$marketplaceId = null){
        if(!isset($data['RequestReportResult']['ReportRequestInfo'])) return false;
        $response = $data['RequestReportResult']['ReportRequestInfo'];
        $requestId = $response['ReportRequestId'];
        $reportRequest = self::_getReportRequest($requestId);
        if($reportRequest)return $reportRequest;
        $res = [];
        $res['account_id'] = $accountId;
        $res['type_id'] = ReportType::getReportTypeIdByType($response['ReportType']);
        $res['request_id'] = $response['ReportRequestId'];
        $res['submitted_at'] = gmdate('Y-m-d H:i:s',strtotime($response['SubmittedDate']));
        $marketplaceId && $res['marketplace_id'] = $marketplaceId;
        $res['start_at'] = gmdate('Y-m-d H:i:s',strtotime($response['StartDate']));
        $res['end_at'] = gmdate('Y-m-d H:i:s',strtotime($response['EndDate']));
        return ReportRequest::query()->create($res);
    }

    private static function _getReportRequest($requestId){
        return ReportRequest::query()->where('request_id',$requestId)->first();
    }

    /**
     * 请求报告列表，报告下载
     * API GetReportRequestList
     * API GetReport
     * @param $account
     * @return bool
     */
    public static function getReport($account){
        if(!$account)return false;
        //获取请求报告id
        $needDealReportRequestIds = self::_getNeedDealReportRequestIds($account->id);
        if(!$needDealReportRequestIds)return false;

        //客户端请求
        $service = self::getRequestClient('report',$account);

        //报告类型/参数
        $rm = ReportMws::getInit();
        $param = $rm->getReportRequestList(['Merchant'=>$account->merchant,'ReportRequestIdList'=>$needDealReportRequestIds,'MWSAuthToken' => $account->mws_auth_token]);
        $data = $service->requestExe($param);
        if(!$data)return false;

        //报告令牌
        $nextToken = self::_extractGetReportRequestListResponse($account,$service,$data);

        //下载报告
        self::_getReportRequestListByNextToken($account,$service,$nextToken);
        return true;
    }

    /**
     * 报告请求列表ByNextToken，报告下载
     * API GetReportRequestList
     * API GetReport
     * @param $account
     * @param $service
     * @param $nextToken
     */
    private static function _getReportRequestListByNextToken($account,$service,$nextToken){
        //令牌存在,则执行
        if($nextToken){
            sleep(1);
            if(!($service instanceof RequestClient)){
                $service = self::getRequestClient('report',$account);
            }
            $rm = ReportMws::getInit();
            $param = $rm->getReportRequestListByNextToken(['Merchant'=>$account->merchant,'NextToken'=>$nextToken,'MWSAuthToken' =>$account->mws_auth_token]);
            $data = $service->requestExe($param);

            //报告令牌
            $nextToken = self::_extractGetReportRequestListResponse($account,$service,$data,true);

            //下载报告（递归调用自身）
            self::_getReportRequestListByNextToken($account,$service,$nextToken);
        }
    }

    /**
     * 更新报告数据,并获取下次执行令牌($nextToken)
     * @param $account
     * @param $service
     * @param $data
     * @param false $flag
     * @return null
     */
    private static function _extractGetReportRequestListResponse($account,$service,$data,$flag = false){
        $_str = $flag ? 'GetReportRequestLisByNextTokenResult' : 'GetReportRequestListResult';
        $response = isset($data[$_str]['ReportRequestInfo']) ? $data[$_str]['ReportRequestInfo'] : [];
        if(isset($response['ReportRequestId']))$response = [$response];

        //生成报告文件
        self::_extractResult($account,$service,$response);

        $hasNext = isset($data[$_str]['HasNext']) ? $data[$_str]['HasNext'] : null;
        return $hasNext&&isset($data[$_str]['NextToken']) ? $data[$_str]['NextToken'] : null;
    }

    /**
     * 生成报告文件(临时)
     * 更新请求状态
     * 1：待请求，2：完成，3：_CANCELLED_ || _DONE_NO_DATA_，4：请求中，5：_DONE_ 无ReportId，6：下载报告失败
     * @param $account
     * @param $service
     * @param $responses
     */
    private static function _extractResult($account,$service,$responses){
        try{
            DB::connection('mysql_amazon')->beginTransaction();
            foreach($responses as $response){
                $processingStatus = $response['ReportProcessingStatus'];
                if($processingStatus == '_DONE_'){
                    $reportId = isset($response['GeneratedReportId']) ? $response['GeneratedReportId'] : null;
                    if($reportId){
                        sleep(1);
                        //下载报告
                        $status = self::_getReport($account,$service,$reportId);

                        //加载报告（写入加载报告表）
                        self::_storeReport($status,$account->id,$reportId,$response['ReportRequestId'],$response['ReportType']);
                    }else{
                        $status = 5;
                    }
                }elseif($processingStatus == '_CANCELLED_'||$processingStatus == '_DONE_NO_DATA_'){
                    $status = 3;
                }else{
                    $status = 1;
                }

                //回写报告请求处理状态
                self::_writeBackReportRequest($response['ReportRequestId'],$status);
                DB::connection('mysql_amazon')->commit();
            }
        }catch (\Exception $e){
            DB::connection('mysql_amazon')->rollBack();
            report($e);
        }
    }

    /**
     * 下载报告
     * API GetReport
     * @param $account
     * @param $service
     * @param $reportId
     * @return int
     */
    private static function _getReport($account,$service,$reportId){
        if(!($service instanceof RequestClient)){
            $service = self::getRequestClient('report',$account);
        }
        $rm = ReportMws::getInit();
        $param = $rm->getReport(['Merchant'=>$account->merchant,'ReportId'=>$reportId,'MWSAuthToken' =>$account->mws_auth_token]);
        $fileName = $account->id.DIRECTORY_SEPARATOR.$reportId.'.txt';
        $i = 1;
        $flag = false;
        while (!$flag && $i <= 5){
            $flag = $service->requestExe($param,$fileName);
            if(!$flag)$i++;
            sleep(2);
        }
        return $flag ? 2 : 6;
    }

    /**
     * 写入加载报告数据
     * @param $status
     * @param $accountId
     * @param $reportId
     * @param $reportRequestId
     * @param $type
     */
    private static function _storeReport($status,$accountId,$reportId,$reportRequestId,$type){
        if($status == 2){
            $reportLoadCount = self::_getReportLoadCount($reportId);
            if($reportLoadCount == 0){
                $marketplaceId = self::_getReportRequestMarketplaceId($reportRequestId);
                $res = [];
                $res['account_id'] = $accountId;
                $res['report_id'] = $reportId;
                $res['type_id'] = ReportType::getReportTypeIdByType($type);
                $marketplaceId && $res['marketplace_id'] = $marketplaceId;
                ReportLoad::query()->create($res);
            }
        }
    }

    /**
     * 回写报告请求处理状态
     * @param $reportRequestId
     * @param $status
     */
    private static function _writeBackReportRequest($reportRequestId,$status){
        $rr = ReportRequest::query()->where('request_id',$reportRequestId)->first();
        $rr->status = $status;
        if($status == 5){
            $rr->f_tries += 1;
        }elseif ($status == 6){
            $rr->s_tries += 1;
        }
        $rr->save();
    }

    /**
     * 获取报告数量（个数）
     * @param $reportId
     * @return int
     */
    private static function _getReportLoadCount($reportId){
        return ReportLoad::query()->where('report_id',$reportId)->count();
    }

    /**
     * 获取报告请求id
     * @param $reportRequestId
     * @return mixed
     */
    private static function _getReportRequestMarketplaceId($reportRequestId){
        return ReportRequest::query()->where('request_id',$reportRequestId)->value('marketplace_id');
    }

    /**
     * 获取需要生成报告的报告请求IDs
     * @param $accountId
     * @return array|null
     */
    private static function _getNeedDealReportRequestIds($accountId){
        $date = date('Y-m-d H:i:s',time() - self::GRAB_ZONE_INTERVAL - 2 * 60);
        $re = ReportRequest::query()->where('status',1)
            ->where('account_id',$accountId)
            ->where('submitted_at','<=',$date)
            ->take(10)
            ->pluck('request_id','id')
            ->toArray();
        if(empty($re))return null;
        $flag = ReportRequest::query()->whereIn('id',array_keys($re))->update(['status'=>4]);
        if($flag){
            return array_values($re);
        }else{
            return null;
        }
    }

    /**
     * 获取报告列表
     * API GetReportList
     * @param $account
     * @param array $con
     * @return bool
     */
    public static function getReportList($account,$con = array()){
        if(!$account)return false;
        if(!(isset($con['typeIds']) && $con['typeIds']))return false;
        if(!isset($con['AvailableFromDate']) || !$con['AvailableFromDate'])return false;
        if(!isset($con['AvailableToDate']) || !$con['AvailableToDate'])return false;
        if(strtotime($con['AvailableFromDate']) >= strtotime($con['AvailableToDate']))return false;
        $con['ReportTypeList'] = ReportType::getReportTypeListByTypeIds($con['typeIds']);
        $con['MaxCount'] = 50;
        $con['Merchant'] = $account->merchant;
        $con['MWSAuthToken'] = $account->mws_auth_token;
        $service = self::getRequestClient('report',$account);
        $rm = ReportMws::getInit();
        $param = $rm->getReportList($con);
        $data = $service->requestExe($param);
        $nextToken = self::_extractGetReportListResponse($account,$service,$data);
        self::_getReportListByNextToken($account,$service,$nextToken);
        return true;
    }

    /**
     * 获取报告列表响应
     * @param $account
     * @param $service
     * @param $data
     * @param false $flag
     * @return null
     */
    private static function _extractGetReportListResponse($account,$service,$data,$flag = false){
        $_str = $flag ? 'GetReportListByNextTokenResult' : 'GetReportListResult';
        $response = isset($data[$_str]['ReportInfo']) ? $data[$_str]['ReportInfo'] : [];
        if(isset($response['ReportRequestId']))$response = [$response];
        self::_extract2Result($account,$service,$response);
        $hasNext = isset($data[$_str]['HasNext']) ? $data[$_str]['HasNext'] : null;
        return $hasNext&&isset($data[$_str]['NextToken']) ? $data[$_str]['NextToken'] : null;
    }

    /**
     * 生成报告文件
     * @param $account
     * @param $service
     * @param $responses
     */
    private static function _extract2Result($account,$service,$responses){
        try{
            DB::connection('mysql_amazon')->beginTransaction();
            foreach($responses as $response){
                $reportId = isset($response['ReportId']) ? $response['ReportId'] : null;
                if($reportId){
                    sleep(5);
                    $status = self::_getReport($account,$service,$reportId);
                    self::_storeReport($status,$account->id,$reportId,$response['ReportRequestId'],$response['ReportType']);
                }
                DB::connection('mysql_amazon')->commit();
            }
        }catch (\Exception $e){
            DB::connection('mysql_amazon')->rollBack();
            report($e);
        }
    }

    /**
     * 获取报表令牌
     * API GetReportListByNextToken
     * @param $account
     * @param $service
     * @param $nextToken
     */
    private static function _getReportListByNextToken($account,$service,$nextToken){
        if($nextToken){
            sleep(1);
            if(!($service instanceof RequestClient)){
                $service = self::getRequestClient('report',$account);
            }
            $oc = ReportMws::getInit();
            $param = $oc->getReportListByNextToken(['Merchant'=>$account->merchant,'nextToken'=>$nextToken,'MWSAuthToken' =>$account->mws_auth_token]);
            $data = $service->requestExe($param);
            $nextToken = self::_extractGetReportListResponse($account,$service,$data,true);
            self::_getReportListByNextToken($account,$service,$nextToken);
        }
    }
}
